package main

import (
	"context"
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/yaml.v3"
	"goshell.straysh.com/internal/conf"
	"goshell.straysh.com/internal/ioc"
	"goshell.straysh.com/internal/routes"
	"goshell.straysh.com/internal/routes/api"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

var (
	flagconf string
)

func init() {
	flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
}

func main() {
	tstart := time.Now()
	flag.Parse()
	ioc.New()
	setConf(flagconf)
	setLogger()

	addr := ioc.Config().Server.Http.Addr
	apis := routes.NewRouter("/api", func(r *gin.RouterGroup) {
		api.RegisterRoutes(r)
	})
	srv := &http.Server{
		Addr:           addr,
		Handler:        apis,
		WriteTimeout:   ioc.Config().Server.Http.Timeout,
		MaxHeaderBytes: 1 << 20,
	}
	go func() {
		fmt.Printf("server listening http://%s/\n", addr)
		ioc.Logger().Info("server started!", zap.String("time used", time.Since(tstart).String()))
		err := srv.ListenAndServe()
		if err == http.ErrServerClosed {
			fmt.Println(err)
			return
		}
		if err != nil {
			panic(err)
		}
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	ioc.Logger().Info("Shutdown Server ...")

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		ioc.Logger().Fatal("Server Shutdown", zap.Error(err))
	}
	ioc.Logger().Info("Server exiting")
}

func setLogger() *zap.Logger {
	encoder := zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stack",
		EncodeTime:     zapcore.RFC3339TimeEncoder, /*zapcore.ISO8601TimeEncoder*/
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
	core := zapcore.NewCore(
		zapcore.NewConsoleEncoder(encoder),
		zapcore.NewMultiWriteSyncer(
			zapcore.AddSync(os.Stdout),
			//zapcore.AddSync(w),
		), zapcore.DebugLevel)
	z := zap.New(core,
		zap.AddStacktrace(zap.NewAtomicLevelAt(zapcore.ErrorLevel)),
		zap.AddCaller(),
		zap.AddCallerSkip(0),
		zap.Development(),
	)
	ioc.WithLogger(z)(ioc.Ioc())
	return z
}

func setConf(f string) *conf.Bootstrap {
	data, err := ioutil.ReadFile(f)
	if err != nil {
		panic(err)
	}
	var c conf.Bootstrap
	err = yaml.Unmarshal(data, &c)
	if err != nil {
		panic(fmt.Errorf("unmarshal config file: %s", err))
	}
	ioc.WithConfig(&c)(ioc.Ioc())
	return &c
}
